package models;

import java.util.*;

import javax.persistence.*;

import controllers.Application;

import play.db.jpa.*;

import play.modules.search.Field;
import play.modules.search.Indexed;

/**
 * 
 * @author Mostafa Fahim
 * @version 1.0
 * 
 */
@Entity
@Indexed
public class Idea extends Model {

	@Field
	public String title;
	@Lob
	@Field
	public String content;
	public double rating;

	public boolean isActive;
	public boolean isHidden;

	public Date date;
	@ManyToMany
	public List<Plan> plans;
	@OneToMany(mappedBy = "marked", cascade = CascadeType.PERSIST)
	public List<DuplicateMark> duplicateMarksAsMarked;
	@OneToMany(mappedBy = "original", cascade = CascadeType.PERSIST)
	public List<DuplicateMark> duplicateMarksAsOriginal;
	@ManyToMany
	public List<User> subscribers;
	@ManyToOne
	public Topic topic;
	@ManyToOne
	public User creator;
	@OneToMany(mappedBy = "idea", cascade = CascadeType.PERSIST)
	public List<Comment> comments;
	@ManyToMany
	public List<Tag> tags;// ana 7ablas added this line for my own method
	@OneToMany(mappedBy = "idea", cascade = CascadeType.PERSIST)
	public List<IdeaRating> ideaRatings;
	@OneToMany(mappedBy = "sharedIdea", cascade = CascadeType.PERSIST)
	public List<SharedIdea> sharedIdeas;
	public boolean isDraft;
	/** 2 = high priority, 1 = normal, 0 = low */
	public int priority;
	public int numberOfView;
	@ManyToMany(cascade = CascadeType.PERSIST)
	public List<Idea> duplicateIdeas;
	@OneToMany(mappedBy = "firstIdea", cascade = CascadeType.PERSIST)
	public List<MergeRequest> asFirstIdeaMerged;
	@OneToMany(mappedBy = "secondIdea", cascade = CascadeType.PERSIST)
	public List<MergeRequest> asSecondIdeaMerged;
	public LinkedList<String> videos;

	@ManyToOne
	public Organization featuredIn;
	@ManyToMany
	public List<User> featuredFor;

	public boolean isPromoted;

	/**
	 * 
	 * @param title
	 *            title of the idea
	 * @param content
	 *            what is the idea about
	 * @param rating
	 *            current rating of the idea (likeness vs hating)
	 * @param date
	 *            date at which the idea was created
	 * @param plans
	 *            the list of plans associated with the idea
	 * @param topic
	 *            the topic under which the idea was created
	 * @param creator
	 *            the user who created the idea
	 */

	public Idea(String title, String content, Date date, Topic topic,
			User creator) {
		this.title = title;
		this.content = content;
		this.rating = 0;
		this.date = date;
		this.plans = new ArrayList<Plan>();
		this.topic = topic;
		this.creator = creator;
		this.featuredIn = topic.entity.organization;
		this.duplicateMarksAsMarked = new ArrayList<DuplicateMark>();
		this.duplicateMarksAsOriginal = new ArrayList<DuplicateMark>();
		this.tags = new ArrayList<Tag>();
		this.subscribers = new ArrayList<User>();
		this.comments = new ArrayList<Comment>();
		ideaRatings = new ArrayList<IdeaRating>();
		this.isDraft = false;
		this.numberOfView = 0;
		this.duplicateIdeas = new LinkedList<Idea>();
		this.isHidden = false;
		this.videos = new LinkedList<String>();
		this.isPromoted = false;
		featuredFor = new ArrayList<User>();
		asSecondIdeaMerged = new ArrayList<MergeRequest>();
		asFirstIdeaMerged = new ArrayList<MergeRequest>();
	}

	/**
	 * This is a different constructor that has one more parameter than the
	 * first constructor isDraft, which identifies the idea if its saved as a
	 * draft or just posted.
	 * 
	 * @author Hesham Massoud
	 * 
	 * @param title
	 *            title of the idea
	 * @param content
	 *            what is the idea about
	 * @param rating
	 *            current rating of the idea (likeness vs hating)
	 * @param date
	 *            date at which the idea was created
	 * @param plans
	 *            the list of plans associated with the idea
	 * @param topic
	 *            the topic under which the idea was created
	 * @param creator
	 *            the user who created the idea
	 * @param isDraft
	 *            Is the idea saved as a draft or posted.
	 */

	public Idea(String title, String content, Date date, ArrayList<Plan> plans,
			Topic topic, User creator, boolean isDraft) {
		this.title = title;
		this.content = content;
		this.rating = 0;
		this.date = date;
		this.plans = plans;
		this.topic = topic;
		this.creator = creator;
		this.isDraft = isDraft;
		this.duplicateMarksAsMarked = new ArrayList<DuplicateMark>();
		this.duplicateMarksAsOriginal = new ArrayList<DuplicateMark>();
		this.tags = new ArrayList<Tag>();
		this.subscribers = new ArrayList<User>();
		this.comments = new ArrayList<Comment>();
		ideaRatings = new ArrayList<IdeaRating>();
		this.videos = new LinkedList<String>();
		this.isPromoted = false;
		this.featuredIn = topic.entity.organization;
		featuredFor = new ArrayList<User>();
		asSecondIdeaMerged = new ArrayList<MergeRequest>();
		asFirstIdeaMerged = new ArrayList<MergeRequest>();
	}

	/**
	 * method to add new ideaRating instance to the list of idea ratings
	 * 
	 * @param iR
	 *            the new IdeaRating instance
	 * @return an instance of this Idea.
	 */
	public Idea addIdeaRating(IdeaRating iR) {
		ideaRatings.add(iR);
		this.save();
		return this;
	}

	/**
	 * A method to get a list of ideas.
	 * 
	 * @return a list of ideas
	 */

	public static List<Idea> getListOfAllIdeas() {
		return Idea.findAll();
	}

	/**
	 * A method to get a list of ideas given tag.
	 * 
	 * @param tag
	 *            The name of the tag that the method searches for ideas by.
	 * @return A List of ideas that either has a tag with the same name given as
	 *         parameter or a tag with a name that contains the given name.
	 */
	public static List<Idea> searchByTag(String tag) {
		List<Idea> listOfAllIdeas = Idea.findAll();
		List<Idea> resultList = new ArrayList<Idea>();

		for (int i = 0; i < listOfAllIdeas.size(); i++) {
			Idea idea = listOfAllIdeas.get(i);
			for (int j = 0; j < idea.tags.size(); j++) {
				if (idea.tags.get(j).name.contains(tag)) {
					resultList.add(idea);
				}
			}
		}
		return resultList;
	}

	/**
	 * it updates the overall rating of this Idea.
	 * 
	 * @param rating2
	 *            the new rating.
	 * @return Idea, the current instance.
	 * 
	 * @author Ahmed Mahmoud
	 */
	public Idea updateRating(double rating2) {
		rating = rating2;
		save();
		return this;
	}

	/**
	 * A method to add a {@link DuplicateMark} to this instance of {@link Idea}
	 * where this idea is the one marked as duplicate.
	 * 
	 * @param dup
	 *            is the {@link DuplicateMark} that marks this {@link Idea} as
	 *            duplicate.
	 * @return this instance of {@link Idea}
	 * */
	public Idea addDuplicateMarkAsMarked(DuplicateMark dup) {
		duplicateMarksAsMarked.add(dup);
		this.save();
		return this;
	}

	/**
	 * A method to add a {@link DuplicateMark} to this instance of {@link Idea}
	 * where this idea is the original one for which a duplicate was observed.
	 * 
	 * @param dup
	 *            is the {@link DuplicateMark} that marks this {@link Idea} as
	 *            the original one.
	 * @return this instance of {@link Idea}
	 * */
	public Idea addDuplicateMarkAsOriginal(DuplicateMark dup) {
		duplicateMarksAsOriginal.add(dup);
		this.save();
		return this;
	}

	/**
	 * the method is for adding a tag for Idea
	 * 
	 * @param name
	 *            the name of the tag
	 * @return the tagged idea
	 */

	public Idea addTag(String name) {
		Tag tag = Tag.find("byName", name).first();
		if (tag == null)
			tag = new Tag(name);
		for (int i = 0; i < tags.size(); i++) {
			if (tags.get(i).name == tag.name) {
				return this;
			}
		}
		tags.add(tag);
		tag.save();
		this.save();
		return this;
	}

	/**
	 * @author AkrAmAtion
	 * 
	 * @return a String that will be used to export search results displaying an
	 *         idea that matches a keyword. The String is formatted as
	 *         Comma-Separated-Values
	 * */
	public String toString() {
		return "IDEA," + title + "," + content + "," + rating + "," + date
				+ "," + creator;
	}

	/**
	 * checks that the idea was posted within a week and has new comments (in
	 * terms of the date of the comment was within a week)
	 * 
	 * @return boolean, true if the idea is active. false if the idea is
	 *         inactive.
	 * @author Ahmed Mahmoud
	 */
	public boolean isActive() {
		if (Application.isNewDate(this.date.getTime())) {
			return true;
		} else if (this.containsNewComments()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * checks if this idea has new comments (in terms of the date of the comment
	 * was within a week)
	 * 
	 * @return boolean, true if the idea has new comments. false if the comments
	 *         are old.
	 * 
	 * @author Ahmed Mahmoud
	 */
	public boolean containsNewComments() {
		List<Comment> comments = this.comments;
		for (Comment comment : comments) {

			if (Application.isNewDate(comment.time.getTime())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @author AkrAmAtion
	 * 
	 *         adds an idea to the list of duplicate ideas that is associated
	 *         with this {@link Idea}. Indicates that the added idea was
	 *         proposed by a {@link DuplicateMark}
	 * 
	 * @param duplicate
	 *            is the {@link Idea} that is to be added to the list of
	 *            duplicateIdeas
	 * */
	public void addDuplicateIdea(Idea duplicate) {
		if (duplicateIdeas == null) {
			duplicateIdeas = new LinkedList<Idea>();
		}
		duplicateIdeas.add(duplicate);
		this.save();
	}

	/**
	 * the method is for removing a tag from an Idea
	 * 
	 * @author AhmedMustafaTaha
	 * @param name
	 *            the name of the tag
	 * @return the tagged idea
	 */

	public Idea removeTag(Long tagID) {
		Tag tag = Tag.findById(tagID);
		tags.remove(tag);
		this.save();
		if (tag.relatedTopics.size() + tag.relatedIdeas.size()
				+ tag.relatedActionItems.size() == 0) {
			tag.cascadeDelete();
		}
		return this;
	}

	/**
	 * Returns a substring of the content of the idea with length maximum 40
	 * chars
	 * 
	 * @author Islam Al-Aarag
	 * @story C4S66
	 * @return String a substring of the content of the idea
	 */
	public String getSummarizedContent() {
		if (content.length() < 40)
			return content;
		else
			return content.substring(0, 40) + "...";
	}

	/**
	 * @author Ahmed Mahmoud
	 * @return
	 */
	public boolean cascadeDelete() {
		for (Plan plan : plans) {
			plan.ideas.remove(this);
			plan.save();
		}
		while (!duplicateMarksAsMarked.isEmpty()) {
			DuplicateMark duplicateMark = duplicateMarksAsMarked.get(0);
			duplicateMark.cascadeDelete();
		}
		while (!duplicateMarksAsOriginal.isEmpty()) {
			DuplicateMark duplicateMark = duplicateMarksAsOriginal.get(0);
			duplicateMark.cascadeDelete();
		}
		for (User subscriber : subscribers) {
			subscriber.subscribedIdeas.remove(this);
			subscriber.save();
		}
		topic.ideas.remove(this);
		topic.save();
		creator.createdIdeas.remove(this);
		creator.save();
		while (!comments.isEmpty()) {
			Comment comment = comments.get(0);
			comment.cascadeDelete();
		}
		for (Tag tag : tags) {
			tag.relatedIdeas.remove(this);
			tag.save();
		}
		while (!ideaRatings.isEmpty()) {
			IdeaRating ideaRating = ideaRatings.get(0);
			ideaRating.cascadeDelete();
		}
		while (!sharedIdeas.isEmpty()) {
			SharedIdea sharedIdea = sharedIdeas.get(0);
			sharedIdea.cascadeDelete();
		}

		for (User user : featuredFor) {
			user.featuredIdeas.remove(this);
			user.save();
		}
		while (!asSecondIdeaMerged.isEmpty()) {
			MergeRequest mr = asSecondIdeaMerged.get(0);
			mr.cascadeDelete();
		}
		while (!asFirstIdeaMerged.isEmpty()) {
			MergeRequest mr = asFirstIdeaMerged.get(0);
			mr.cascadeDelete();
		}
		featuredIn.organizationFeaturedIdeas.remove(this);
		featuredIn.save();

		this.delete();
		return true;
	}

	public boolean isAllowedToBeViewedToIdeaDevelopers() {
		return !this.isDraft && !this.isHidden;
	}

	/**
	 * use the method Application.dateMap()
	 * 
	 * @return a String representing the date in the format of a period ago.
	 * 
	 * @author AkrAmAtion
	 * */
	public String getPublishedSince() {
		return Application.dateMap(date);
	}

}
